﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Controls.Primitives;
using System.Windows.Navigation;

using Heaven.Graphics;
using Heaven.Engine.Animation;
using Heaven.Geometry.Procedural;
using Heaven.Geometry;
using Heaven.Mathematics;
using Microsoft.Win32;
using Color = Heaven.Graphics.Color;
using CameraBrowser = Heaven.Engine.CameraBrowser;
using Heaven.Engine;
using Heaven.Geometry.Importers;
using Visual = Heaven.Engine.Visual;



namespace Heaven.Showcase.Demos
{
    /// <summary>
    /// Demonstrations. Basic features
    /// </summary>
	public partial class Basics
    {
        #region Fields

        // Heaven device compatible with 
        // Windows Presentation Foundation
        //Heaven.Xna.Graphics.WindowsPresentationDevice device = null;
        Heaven.SlimDX.Graphics.WindowsPresentationDevice device = null;
        // Scene, as a container for objects
        Scene scene = null;

        // Camera with particular behaviour
        CameraBrowser camera = new CameraBrowser();
        // It's for dragging
        double mouseX = Double.NaN;
        double mouseY = Double.NaN;

        // Flag means that redraw needed
        bool invalidated = true;
        // Timer to perform rendering when invalidated is set
        DispatcherTimer timer = new DispatcherTimer(DispatcherPriority.Normal);

        #endregion

        #region Initializations

        /// <summary>
        /// Constructor
        /// </summary>
        public Basics()
		{
			this.InitializeComponent();
            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.Unloaded += new RoutedEventHandler(OnUnloaded);
            targetImage.SizeChanged += new SizeChangedEventHandler(OnSizeChanged);
            targetImage.MouseMove += new MouseEventHandler(OnMouseMove);
  
            // Initialize the timer
            //timer.Interval = TimeSpan.FromSeconds(0.02);
            //timer.Tick += new EventHandler(OnTimerTick);

            // Initialization of graphics device
            //device = new Heaven.Xna.Graphics.WindowsPresentationDevice(targetImage);
            device = new Heaven.SlimDX.Graphics.WindowsPresentationDevice(targetImage, Render);
            device.DefaultColor = new Color(0, 0, 0, 0); 

            // Tuning the scene
            scene = new Scene(camera);
            camera.HorisontalRotation = 0.84f;
            camera.VerticalRotation = 1.07f;
            camera.Target = Vector3.Zero;
            camera.Distance = 170;
        }

        #endregion

        #region Materials construction

        /*Technique CreateMaterial()
        {
            // Creating custom node
            
            InputValueNode input = new InputValueNode(SocketTypes.FloatFour);
            OutputValueNode output = new OutputValueNode(SocketTypes.FloatThree);
            
            CustomGPUNode gpunode = new CustomGPUNode();
            Socket inputSocket = new Socket(SocketTypes.FloatFour);
            Socket outputSocket = new Socket(SocketTypes.FloatThree);
            gpunode.Inputs.Add(inputSocket);
            gpunode.Outputs.Add(outputSocket);
            gpunode.Code = "float3 f = lod2d(.....";
             
            Graph graph = new Graph();
            graph.Nodes.Add(input);
            graph.Nodes.Add(output);
            graph.Nodes.Add(gpunode);
            graph.Connectors.Add(new Connector(input.Out, inputSocket));
            graph.Connectors.Add(new Connector(output.In, outputSocket));
            
            Technique result = new Technique(device);
            result.Pipeline.Add(Graph);
            result.Update();

            return result;
             
            
        }*/

        #endregion

        #region Manipulation routines

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point point = e.GetPosition(this);

                if (!Double.IsNaN(mouseX))
                {
                    camera.HorisontalRotation += (float)((point.X - mouseX) / 100);
                    camera.VerticalRotation += (float)((point.Y - mouseY) / 100);
                    Invalidate();
                }                
                
                mouseX = point.X;
                mouseY = point.Y;
            }
            else
            {
                mouseX = Double.NaN;
                mouseY = Double.NaN;
            }
        }

        void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            camera.Aspect = (float)(e.NewSize.Width / e.NewSize.Height);
            Invalidate();
        }

        void OnTimerTick(object sender, EventArgs e)
        {
            if (invalidated)
            {
                Render();
                invalidated = false;
            }
        }

        void OnUnloaded(object sender, RoutedEventArgs e)
        {
            timer.Stop();
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
            LoadVisuals();
            timer.Start();
        }

        #endregion

        #region Loading geometries

        void LoadVisuals()
        {
            /*
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(
                   "Heaven.Showcase.Resources.Models.Basics.obj"))
            {
                Wavefront loader = Wavefront.Parse(stream);
                foreach (Polygonal polygonal in loader.Objects)
                {
                    Visual visual = new Visual(device, polygonal);
                    scene.Visuals.Add(visual);
                }
            }*/
            OpenFileDialog dialog = new OpenFileDialog();
            if(dialog.ShowDialog() == true)
            {
                using(FileStream stream = new FileStream(dialog.FileName, FileMode.Open, FileAccess.Read))
                {
                    Stereolith stereolith = Stereolith.Parse(stream);
                    stereolith.Mesh.Weld(0.8);
                    stereolith.Mesh.Smooth(40.0 * (Math.PI / 180.0));
                    Visual visual = new Visual(stereolith.Mesh);
                    visual.Technique.Properties["Color"].Value = new Color(0, 1, 0, 1);
                    scene.Visuals.Add(visual);

                    /*Sphere sphere = new Sphere(Vector3.Zero, 50);
                    Polygonal sphereMesh = sphere.Generate();
                    Visual visual = new Visual(device, sphereMesh);
                    scene.Visuals.Add(visual);*/

                    Polygonal mesh = stereolith.Mesh;
                    visual = new Visual(Debug.Normals(mesh));
                    visual.Technique.Properties["Color"] = new Property<Color>(new Color(1,0.5,0.5,1));
                    scene.Visuals.Add(visual);
                }
            }
        }

        #endregion

        #region Rendering

        void Invalidate()
        {
            invalidated = true;
        }

        void Render()
        {
            device.Begin();
            device.Clear();
            scene.Render(device);
            device.Present();
        }

        #endregion
    }
}